import logging, cv2, shutil, os, json
import face_recognition
import numpy as np
from face_recognition.api import face_encoder
from utils.JsonUtil import find_target_json, read_file, write_or_update
from PIL import Image, ImageDraw, ImageFont
import matplotlib.pyplot as plt
from datetime import datetime

# 根目录路径
root_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# json文件路径
data_source_path = os.path.join(root_path, 'data', 'data_face.json')
# 人脸库路径
face_path = os.path.join(root_path, 'resources', 'images', 'face_recognized')


def face_detect_and_save(path):
    """
    人脸识别：保存图片，并写入特征点到json
    :param path: 文件路径
    :return: 文件名
    """
    picture_name = path.split('/')[-1]
    json_data = []
    data = face_detect(path)
    json_data.append(data)
    face_save_to_json(data_source_path, 'id', json_data)
    logging.info('人脸特征已保存到json文件')
    shutil.copy2(path, face_path)
    logging.info('人脸图片拷贝完成')
    return picture_name.split('.')[0]


def face_detect(path):
    """
    人脸特征识别
    :param path: 图片路径
    :return: [dict] 特征点数据
    """
    data = {}
    name = os.path.basename(path)
    data['id'] = name.split('_')[1].split('.')[0]
    data['name'] = name.split('_')[0]
    data['path'] = path
    # 加载图片并转换为RGB格式（face_recognition需要RGB格式）
    image = face_recognition.load_image_file(path)
    face_encoding = face_recognition.face_encodings(image)[0]
    face_locations = face_recognition.face_locations(image)  # 返回图像中的坐标，先不用
    data['features'] = face_encoding.tolist()

    # 显示人脸位置（如果有的话）
    for face_location in face_locations:
        top, right, bottom, left = face_location
        cv2.rectangle(image, (left, top), (right, bottom), (0, 0, 255), 2)

    # 设置点的颜色（BGR格式）和位置
    point_color = (255, 0, 0)  # 绿色
    point_radius = 1  # 点的半径，设置为1以使其看起来像点
    # 检测人脸特征点
    face_landmarks_list = face_recognition.face_landmarks(image)
    for face_landmarks in face_landmarks_list:
        for facial_feature in face_landmarks.keys():
            for features in face_landmarks[facial_feature]:
                cv2.circle(image, features, point_radius, point_color, -1)

    # 显示图片
    # cv2.imshow('Faces', image)
    # cv2.waitKey(0)
    # cv2.destroyAllWindows()
    return data


def face_save_to_json(path, key, data):
    """
    人脸特征保存到json文件
    :param path: 路径
    :param key: 保存的关键字，一般是唯一建，例如身份证号。用户区别要新增还是要修改
    :param data: json数据
    :return: 无返回值
    """
    # JsonUtil.write_file(, data)
    write_or_update(path, key, data)


def detect_compare(path):
    """
    人脸特征跟json库比较
    :param path: 文件路径
    :return: 返回文件名
    """
    unknown_face = face_recognition.load_image_file(path)
    unknown_face_list = face_recognition.face_encodings(unknown_face)
    if len(unknown_face_list) == 0:
        return ''
    unknown_face_encoding = face_recognition.face_encodings(unknown_face)[0]
    file = open(data_source_path, 'r', encoding='utf-8')
    known_faces = json.load(file)
    for item in known_faces:
        # current_face = face_recognition.load_image_file(item['path'])
        # current_encoding = face_recognition.face_encodings(current_face)[0]
        # compare_result = face_recognition.compare_faces([current_encoding], unknown_face_encoding, 0.5)
        # 取保存的特征更快
        compare_result = face_recognition.compare_faces([np.array(item['features'])], unknown_face_encoding, 0.5)
        if compare_result[0]:
            return item['name']
    return ''


def cv2_video():
    """
    摄像头采集图片，并识别人脸
    :return: null
    """
    name = '人脸未录入'
    # 人脸源文件不存在或者存在且为空，则写入[]
    if not os.path.exists(data_source_path) or os.path.getsize(data_source_path) == 0:
        with open(data_source_path, 'w', encoding='utf-8') as f:
            f.write('[]')
    file = open(data_source_path, 'r', encoding='utf-8')
    known_faces = json.load(file)
    # path = os.path.join(face_path, 'video.jpg')
    # 加载人脸检测模型
    face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')

    # 打开摄像头
    cap = cv2.VideoCapture(0)

    while True:
        # 人脸区域的最小、最大 x,y 轴坐标
        min_x = 99999
        min_y = 99999
        max_x = 0

        # 读取一帧视频
        ret, frame = cap.read()
        if not ret:
            break
        # print(datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3])

        # 不保存图片，直接比较json的list, 大约需要0.6秒
        face_encoding = face_recognition.face_encodings(frame)
        if len(face_encoding) > 0:
            for item in known_faces:
                # 取保存的特征更快
                compare_result = face_recognition.compare_faces([np.array(item['features'])], face_encoding[0], 0.5)
                if compare_result[0]:
                    name = item['name']
                    break

        # 将图片转换为灰度图，因为我们使用的是灰度图进行人脸检测
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 检测人脸
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))

        # 在检测到的人脸周围画矩形框
        for (x, y, w, h) in faces:
            if x < min_x:
                min_x = x
            if y < min_y:
                min_y = y
            if x > max_x:
                max_x = x
            cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 0, 0), 2)
            # cv2.putText(frame, name, (int(x + w / 2), y - 10), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 1)

        # 显示结果图像
        cv2.imshow('Face Detection', cv2_image_add_text(frame, name, int((min_x + max_x) / 2), min_y - 60, ))
        # 按 'q' 键退出循环
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    # 释放摄像头资源并关闭所有窗口
    cap.release()
    cv2.destroyAllWindows()


def cv2_image_add_text(img, text, left, top, text_color=(255, 0, 0), text_size=40):
    """
    cv2 在视频帧上写文本不支持中文，把帧传给pil写入中文后，再传回cv2来实现
    :param img: cv2图片
    :param text: 中文文本
    :param left: x轴位置
    :param top: y轴位置
    :param text_color: 文本颜色，默认为黑色
    :param text_size: 文本大小
    :return: cv2图片
    """
    if isinstance(img, np.ndarray):
        img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(img)
        font_text = ImageFont.truetype(os.path.join(root_path, 'resources', 'simsun.ttc'), text_size, encoding="utf-8")
        draw.text((left, top), text, text_color, font=font_text)
        return cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)


if __name__ == "__main__":
    cv2_video()
